#include "skywalking_client.h"
#include "common.h"
#include "helper.h"
#include "segment.h"
#include "tcp_client.h"

#include <string>
#include <netdb.h>
#include <unistd.h>
#include <thread>
#include <sys/socket.h>
#include <errno.h>
#include <curl/curl.h>

SkywalkingClient *SkywalkingClient::newInstance()
{
    if (SkywalkingClient::instance == nullptr)
    {
        SkywalkingClient::instance = new SkywalkingClient();
    }
    return SkywalkingClient::instance;
}

void SkywalkingClient::conn()
{
    SkywalkingClient *skywalking_client = SkywalkingClient::newInstance();
    logger("[skywalking] connect");

    skywalking_client->getTcp();
}

void SkywalkingClient::sendMsg(bool enable)
{
    SkywalkingClient *skywalking_client = SkywalkingClient::newInstance();
    logger("[skywalking] send start");
    int count = 0;
    while (1)
    {
        if (!enable)
        {
            skywalking_client->clean();
        }
        if (!skywalking_client->isEmpty())
        {
            logger("[skywalking] fetch content");
            std::string data = skywalking_client->pop();
            int datalen = data.length();

            std::string request = "";
            std::string uri = skywalking_client->getUri();
            std::string host = skywalking_client->getHost();

            request += "POST " + uri + " HTTP/1.1\r\n";
            request += "Host: " + host + "\r\n";
            request += "Content-Type: application/json\r\n";
            request += "Content-Length: " + std::to_string(datalen) + "\r\n";
            request += "Connection: keep-alive\r\n\r\n";
            request += data;

            TCPClient *tcp = skywalking_client->getTcp();
            bool send_status = tcp->Send(request);
            if (!send_status)
            {
                tcp->exit();
                skywalking_client->delTcp();
                // skywalking_client->push(data);
                logger("[skywalking] send error");
            }
            else
            {
                std::string response = tcp->read();
                logger("[skywalking] fetch response: " + response);
            }
        }
        else
        {
            if (!skywalking_client->isEmptyTcp())
            {
                if (count >= 5)
                {
                    TCPClient *tcp = skywalking_client->getTcp();
                    tcp->exit();
                    skywalking_client->delTcp();
                    count = 0;
                }
                else
                {
                    count++;
                }
            }
            std::this_thread::sleep_for(std::chrono::seconds(1));
        }
    }

    logger("[skywalking] send end");
}

void SkywalkingClient::init()
{
    std::string host = MONITORPRO_G(skyagentip);
    std::string port = MONITORPRO_G(skyagentport);
    int skyqueuesize = MONITORPRO_G(skyqueuesize);
    this->setHost(host);
    this->setPort(port);
    this->setUri("/");
    this->setQueueSize(skyqueuesize);
}

void SkywalkingClient::run()
{
    if (!this->status)
    {
        logger("[skywalking] running");
        bool enable = MONITORPRO_G(skyenable);

        std::thread t(sendMsg, enable);
        t.detach();

        this->status = true;
    }
}

bool SkywalkingClient::isEmpty()
{
    return this->msg.empty();
}

void SkywalkingClient::push(std::string data)
{
    if (this->queueSize > 0 && this->msg.size() > (uint)this->queueSize)
    {
        this->msg.pop_back();
    }
    this->msg.insert(this->msg.begin(), data);
}

std::string SkywalkingClient::pop()
{
    std::string data = this->msg.back();
    this->msg.pop_back();
    return data;
}

std::string SkywalkingClient::getHost()
{
    return this->host;
}

std::string SkywalkingClient::getPort()
{
    return this->port;
}

std::string SkywalkingClient::getUri()
{
    return this->uri;
}

int SkywalkingClient::getSockFd()
{
    return this->sockfd;
}

Segment *SkywalkingClient::getSegment()
{
    if (this->seg == nullptr)
    {
        std::string service = thisService();
        std::string instance = thisInstance();
        std::string header = "";
        int version = MONITORPRO_G(skyversion);

        switch (version)
        {
        case 5:
            header = server_param("HTTP_SW3");
            break;
        case 6:
        case 7:
            header = server_param("HTTP_SW6");
            break;
        case 8:
            header = server_param("HTTP_SW8");
            break;
        default:
            header = "";
            break;
        }

        this->seg = new Segment(service, instance, version, header);
    }
    return this->seg;
}

TCPClient *SkywalkingClient::getTcp()
{
    if (this->tcp == nullptr)
    {
        this->tcp = new TCPClient();
        this->tcp->setup(this->host, std::atoi(this->port.c_str()));
    }
    return this->tcp;
}

void SkywalkingClient::setHost(std::string host)
{
    this->host = host;
}

void SkywalkingClient::setPort(std::string port)
{
    this->port = port;
}

void SkywalkingClient::setUri(std::string uri)
{
    this->uri = uri;
}

void SkywalkingClient::setSockFd(int fd)
{
    this->sockfd = fd;
}

void SkywalkingClient::setQueueSize(int size)
{
    this->queueSize = size;
}

void SkywalkingClient::delSeg()
{
    delete this->seg;
    this->seg = nullptr;
}

void SkywalkingClient::delTcp()
{
    delete this->tcp;
    this->tcp = nullptr;
}

bool SkywalkingClient::isEmptyTcp()
{
    return this->tcp == nullptr ? true : false;
}

void SkywalkingClient::clean()
{
    delete this->seg;
    this->seg = nullptr;
    std::vector<std::string>().swap(this->msg);
}